package com.supreme.nettyapp.generalclient.beatclient.service;

import com.supreme.nettyapp.generalclient.beatclient.handler.BeatClientHandler;
import com.supreme.nettyapp.generalclient.beatclient.handler.BeatClientInitializer;
import com.supreme.nettyapp.generalclient.beatclient.props.BeatClientProps;
import com.supreme.nettyapp.generalclient.service.NettyChannelService;
import com.supreme.nettyapp.generalclient.service.NettyClientService;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Optional;

@Service
public class BeatClientService implements NettyClientService, NettyChannelService {

    @Resource
    private BeatClientProps beatClientProps;

    private Channel clientChannel;

    private Bootstrap bootstrap;
    private EventLoopGroup loopGroup;

    public void buildBootstrap() {
        bootstrap = new Bootstrap();
        loopGroup = new NioEventLoopGroup();
        SingleThreadEventLoop eventLoop = (SingleThreadEventLoop) loopGroup.next();
        BeatClientInitializer clientInitializer = BeatClientInitializer.INSTANCE;
        clientInitializer.setBeatClientHandler(new BeatClientHandler());

        bootstrap.group(eventLoop)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new LoggingHandler(LogLevel.INFO))
                .handler(clientInitializer);
    }

    @Override
    public void startNettyClient() {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .handler(BeatClientInitializer.INSTANCE);

            clientChannel = bootstrap.connect(beatClientProps.getServerHost(), beatClientProps.getServerPort()).sync().channel();
            clientChannel.closeFuture().sync();
        } catch (InterruptedException e) {
            System.out.println("e.getMessage() = " + e.getMessage());
        } finally {
            group.shutdownGracefully();
        }
    }

    @Override
    public void startNettyClient(String host, int port) {
        bootstrap.connect(host, port).addListener((ChannelFutureListener) future -> {
            clientChannel = future.channel();
            System.out.println("isSuccess = " + future.isSuccess());
            System.out.println("isDone = " + future.isDone());
            System.out.println("isVoid = " + future.isVoid());
            System.out.println("isCancelled = " + future.isCancelled());
            System.out.println("isCancellable = " + future.isCancellable());
        });
    }

    public void startNettyClient(String host, int port, BeatClientInitializer clientInitializer) {
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .handler(new LoggingHandler(LogLevel.INFO))
                .handler(clientInitializer);

        bootstrap.connect(host, port).addListener((ChannelFutureListener) future -> {
            clientChannel = future.channel();
            System.out.println("isSuccess = " + future.isSuccess());
            System.out.println("isDone = " + future.isDone());
            System.out.println("isVoid = " + future.isVoid());
            System.out.println("isCancelled = " + future.isCancelled());
            System.out.println("isCancellable = " + future.isCancellable());
        });
    }

    @Override
    public void destroyNettyClient() {

    }

    @Override
    public Channel getChannel() {
        return clientChannel;
    }

    @Override
    public void writeAndFlush(Object msg) {
        Optional.ofNullable(clientChannel).ifPresent(channel -> channel.writeAndFlush(msg));
    }
}
